# coding: utf-8

import datetime
import time
from datetime import timedelta

from sqlalchemy.sql import text, func, distinct, case

from app.extensions import db
from app.models import BaseModel, get_timestamp
from app.constant import get_tomorrow_ten_oclock
from app.op.constant import DemandClientSource, OperateResult, DemandSource, DemandCate, DemandStatus
from app.op.models import DemandClient, User, Demand

from .constant import (
    CallState, CallReason, CallReasonWeight, QueueState,
)


class OutBoundRecord(BaseModel):
    """
    外呼记录
    """
    __tablename__ = 'outbound_records'

    search_keys = ('admin_nickname',)

    filter_dict = {
        'call_state': (),
        'agent_id': (),
        'call_time_begin': ('call_time', '>='),
        'call_time_end': ('call_time', '<='),
        'is_invite_success': (),
        'admin_id': (),
        'demand_id': (),
        'mobile': (),
    }

    # 本地外呼编号
    local_call_id = db.Column(
        db.String(128), nullable=False, unique=True, default='',)
    # 明城的callId
    call_id = db.Column(
        db.String(128), nullable=True, default='',)
    # 明城系统中录音地址，这个是明城返回的原始录音地址，不能直接使用
    # {"recordFile":"20170815\\004\\141457_1_18903718031_18900000000"}
    # 转换为 http://192.168.1.207:8080/Record/20170815/004/141457_1_18903718031_18900000000.wav
    audio_path = db.Column(
        db.String(2048), default='', server_default=text("''"))

    # 外呼号码
    mobile = db.Column(
        db.String(16), nullable=False, index=True,
        default='', server_default=text("''")
    )
    user_id = db.Column(
        db.Integer(),
        db.ForeignKey('user.id'),
        nullable=True,
        default=None,
        index=True
    )
    # 外呼原因 对应constant.CallReason
    call_reason = db.Column(
        db.Integer, nullable=False, server_default=text("'0'"),
        default=CallReason.VISIT_CALLBACK.value,
    )
    # 在外呼队列中的状态 对应constant.QueueState
    queue_state = db.Column(
        db.Integer, nullable=False, server_default=text("'0'"),
        default=QueueState.WAITING.value,
    )
    # 外呼状态 对应constant.CallState
    call_state = db.Column(
        db.Integer, nullable=False, server_default=text("'0'"),
        default=CallState.WAITING.value,
    )
    # 外呼权重
    call_weight = db.Column(
        db.Integer, nullable=False, server_default=text("'0'"), default=0)

    # 实际外呼时间
    call_time = db.Column(db.DateTime(), nullable=True)
    # 计划外呼时间
    schedule_time = db.Column(db.DateTime(), nullable=True)

    # 接听座席
    agent_id = db.Column(
        db.String(128), nullable=False, default='', server_default=text("''"))
    # 接听人ID
    admin_id = db.Column(
        db.String(128), nullable=False, default='', server_default=text("''"))
    # 接听人昵称
    admin_nickname = db.Column(
        db.String(128), nullable=False, default='', server_default=text("''"))
    # 跟进备注
    remark = db.Column(
        db.String(255), default='', server_default=text("''"))

    # 是否已经上传录音
    upload_audio = db.Column(db.Boolean(), default=False)
    # 是否拉新成功
    is_invite_success = db.Column(db.Boolean(), default=False)

    # 导入拉新的客服id
    creator_id = db.Column(db.Integer(), index=True)
    # 项目id（拉新外呼）
    demand_id = db.Column(db.Integer())
    # # 引导客服id
    guide_id = db.Column(db.Integer())
    guide_name = db.Column(db.String(64), default='', server_default=text("''"))
    guide_call_date = db.Column(db.Date())

    # 拉新外呼结果
    call_result = db.Column(db.String(32), default='', server_default=text("''"))

    # 虚拟号记录的ID
    virtual_number_id = db.Column(db.Integer(), nullable=True)
    # 是否使用虚拟号，预留的字段，预防以后部分功能不需要套虚拟号
    need_virtual_number = db.Column(db.Boolean(), default=True, server_default=text("true"))
    # 七陌webcall的action_id
    webcall_action_id = db.Column(db.String(256), default='', server_default=text("''"), index=True)
    # 七陌webcall是否拒接的标识
    webcall_reject = db.Column(db.Boolean(), default=False, server_default=text("false"))

    owner = db.relationship(
        'User',
        primaryjoin='foreign(OutBoundRecord.mobile) == remote(User.mobile)',
        single_parent=True,
        backref=db.backref('outbound_records', lazy='dynamic'),
    )

    @property
    def call_reason_display(self):
        return CallReason(self.call_reason).display

    @property
    def queue_state_display(self):
        return QueueState(self.queue_state).display

    @property
    def call_state_display(self):
        return CallState(self.call_state).display

    @property
    def call_time_display(self):
        return str(self.call_time) if self.call_time else ''

    @property
    def schedule_time_display(self):
        return str(self.schedule_time) if self.schedule_time else ''

    @classmethod
    def update_invite_state(cls, user_id):
        """
        create by ld_wu 2017-09-06
        工程商绑定以后，找到最后打出拉新电话的记录
        并更新拉新的状态为成功
        """
        record = cls.query.filter(
            cls.call_reason == CallReason.INVITE_NEW_USER.value,
            cls.user_id == user_id,
            cls.is_invite_success.__eq__(False)
        ).order_by(
            cls.call_time.desc()
        ).first()
        if record:
            record.is_invite_success = True

    @classmethod
    def has_invite_record(cls, creator_id):
        """
        create by ld_wu 2017-10-16
        客服是否还有未处理的拉新外呼记录
        """
        return cls.get_by_args(
            creator_id=creator_id,
            call_reason=CallReason.INVITE_NEW_USER.value,
            queue_state=QueueState.WAITING.value
        )

    @classmethod
    def make_invite_record(cls, user, mobile, creator_id, demand_id):
        """
        create by ld_wu 2017-10-12
        当该潜在工程商没有待处理拉新外呼记录时，插入拉新记录
        """
        from app.op.constant import ParticStatus
        from app.op.models import Partic
        if not user.wx_openid:
            record = cls.get_by_args(
                user_id=user.id,
                mobile=mobile,
                call_reason=CallReason.INVITE_NEW_USER.value,
                queue_state=QueueState.WAITING.value
            )
            if not record:
                delay_call_time = datetime.datetime.now() + timedelta(days=365)
                OutBoundRecord.create(
                    user_id=user.id,
                    mobile=mobile,
                    local_call_id='{}{}'.format(
                        get_timestamp(), mobile),
                    call_reason=CallReason.INVITE_NEW_USER.value,
                    call_weight=1000,
                    schedule_time=delay_call_time,
                    creator_id=creator_id,
                    demand_id=demand_id
                )
                # 潜在工程商直接享有关联项目的信息
                if not Partic.get_by_args(demand_id=demand_id, supplier_id=user.id):
                    Partic.create(
                        demand_id=demand_id,
                        supplier_id=user.id,
                        status=ParticStatus.SUCCEED.value,
                        is_revert=True
                    )

    @classmethod
    def invite_count(cls, query):
        query = query.with_entities(
            func.count().label('total_call'),
            func.count(distinct(cls.mobile)).label('engineer_count'),
            func.coalesce(func.sum(
                case([(
                    cls.is_invite_success.__eq__(True),
                    1)],
                    else_=0
                )
            ), 0).label('invite_count')
        )
        return query.first()


def create_outbound_mission(
    mobile=None, call_reason=None,
    schedule_time=None, try_again=False, user_id=None,
    enterprise_hp=None
):
    """
    add by zhouhl 2017-08-21
    添加外呼任务，返回OutBoundRecord实例，返回OutBoundRecord实例
    schedule_time 默认次日早上10点
    在 view 中将实例 add 到 db.session 中

    @:param
    enterprise_hp: 若此值不为None, 则表示是通过拨打官网上的工程商虚拟号后进入系统的,
                需要判断该用户是否已发过单,若已发过单,则不外呼,
                但需要进入客户列表,并标识其不外呼原因
    """
    # add by chenhj, 若mobile为7moor的3个号码, 则直接返回
    from app.op.constant import SEMGroup
    from app.axb.models import NumberPoolForLT
    if mobile in (SEMGroup.GroupA.value, SEMGroup.GroupB.value, SEMGroup.GroupC.value):
        return None, None

    # 如果号码为虚拟号, 则直接返回 add by chenhj
    lt_rec = NumberPoolForLT.get_by_args(tel=mobile)
    if lt_rec:
        return None, None

    if enterprise_hp:
        user = User.get_by_args(
            mobile=mobile
        ) or User.create(
            mobile=mobile,
            nickname='万屏汇客户'
        )
        user_id = user.id
        demand_client = DemandClient.visit(
            user_id=user.id,
            client_source=DemandClientSource.MARKETING_CALLIN.value,
            enterprise_id=enterprise_hp.enterprise_id
        )
        # 已存在有效订单,不再插入外呼任务
        existed_demand = Demand.query.filter(
            Demand.uid == user.id,
            Demand.status != DemandStatus.INVALID.value
        ).first()
        if existed_demand:
            # 但若订单非"草稿/待审核"状态, 才修改处理结果为已处理
            if existed_demand.status not in (DemandStatus.DRAFT.value, DemandStatus.UNCHECKED.value):
                demand_client.operate_result = OperateResult.DONE_CAUSE_DUPLICATE.value
            return None, None
        # 插入一条草稿单(默认安装),并表示该单来自此企业
        Demand.create(
            uid=user.id,
            area_id=user.area_id,
            cate=DemandCate.INSTALL.value,
            prov=user.prov,
            city=user.city,
            source=DemandSource.MARKETING_CALLIN.value,
            enterprise_id=enterprise_hp.enterprise_id
        )

    new_outbound_record = None
    err = None

    mobile = mobile.strip()
    local_call_id = str(int(time.time())) + mobile
    call_weight = CallReasonWeight.get(call_reason, 1000)
    if not schedule_time:
        schedule_time = get_tomorrow_ten_oclock()

    # 判断当天是否存在相同外呼原因的外呼任务
    # NOTE by zhouhl 临时的修复方案
    if try_again:
        waiting_outbound_record = None
    else:
        waiting_outbound_record = OutBoundRecord.query.filter(
            OutBoundRecord.mobile == mobile,
            # OutBoundRecord.call_reason == call_reason,
            OutBoundRecord.queue_state == QueueState.WAITING.value,
            OutBoundRecord.schedule_time >= datetime.date.today(),
            OutBoundRecord.schedule_time <= datetime.date.today() + datetime.timedelta(days=1),
        ).first()

    # 新建外呼任务
    if not waiting_outbound_record:
        new_outbound_record = OutBoundRecord(
            local_call_id=local_call_id,
            mobile=mobile,
            call_reason=call_reason,
            call_weight=call_weight,
            schedule_time=schedule_time,
            user_id=user_id
        )
        print(new_outbound_record)
    else:
        if waiting_outbound_record:
            err = '该号码已存在待处理的外呼任务'
        else:
            err = '该号码在最近15天已进行外呼'

    return new_outbound_record, err


def cancel_outbound_mission(mobile):
    """
    created by chenhj at 28/02/2018
    取消某电话的外呼任务(手动延后外呼的不取消)
    """
    wait_outbound_missions = OutBoundRecord.query.filter(
        OutBoundRecord.mobile == mobile,
        OutBoundRecord.queue_state == QueueState.WAITING.value,
        OutBoundRecord.call_reason != CallReason.DELAY_RECALL_CLIENT.value
    ).all()

    for each in wait_outbound_missions:
        each.queue_state = QueueState.CANCEL.value

